package com.hospital.aspect;

import com.hospital.utils.Constants;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 日志切面类，请都使用"info"级别的输出。否则就需要更改配置文件的输出级别
 *
 * @Author yuhuan
 * @Date 2022/4/1
 */
@Component
@Aspect
@Slf4j
public class LogAspect {

    /**
     * 给所有的业务代码添加前置日志通知
     */
    @Before("PointCutConcentrationCamp.toServiceAllMethod()")
    public void weavingWebLogBefore(JoinPoint joinPoint) {
        String classAndMethodName = getClassAndMethodNameFromJoinPoint(joinPoint);
        // 控制器执行前的日志记录
        log.info(">>>>> 开始处理业务&" + classAndMethodName);
    }

    /**
     * 给所有的业务代码添加后置日志通知
     */
    @After("PointCutConcentrationCamp.toServiceAllMethod()")
    public void weavingWebLogAfter(JoinPoint joinPoint) {
        String classAndMethodName = getClassAndMethodNameFromJoinPoint(joinPoint);
        // 控制器执行完成后的日志记录
        log.info(">>>>> 业务处理结束&" + classAndMethodName);
    }

    /**
     * 给所有的控制器添加日志记录
     */
    @Around("PointCutConcentrationCamp.toControllerAllMethod()")
    public Object weavingServiceLog(ProceedingJoinPoint joinPoint) {
        Object value = null;
        Class<?> aClass = joinPoint.getTarget().getClass();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String classAndMethodName = getClassAndMethodName(aClass, signature);
        // 业务类执行前的日志输出
        log.info(">>>>> 控制器开始执行&" + classAndMethodName);
        try {
            // 回调到通知链形成回调循环，直到通知栈执行完毕就会执行引用点
            value = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        // 业务类执行完毕后的日志输出
        log.info(">>>>> 控制器执行完毕&" + classAndMethodName);
        return value;
    }

    /**
     * 从连接点提取类和方法名
     *
     * @param joinPoint 通知方法的连接点
     * @return 目标对象的全限类名和方法名
     */
    private String getClassAndMethodNameFromJoinPoint(JoinPoint joinPoint) {
        Class<?> aClass = joinPoint.getTarget().getClass();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String classAndMethodName = getClassAndMethodName(aClass, signature);
        return classAndMethodName;
    }

    /**
     * 获取目标对象的全限类名和方法名
     *
     * @param aClass    目标对象的类对象
     * @param signature 目标方法的方法签名
     * @return 用全限类名和方法名拼接的完整的字符串
     */
    private String getClassAndMethodName(Class<?> aClass, MethodSignature signature) {
        StringBuilder stringBuilder = new StringBuilder();
        try {
            Method method = aClass.getDeclaredMethod(signature.getName(), signature.getParameterTypes());
            String className = method.getDeclaringClass().getName();
            stringBuilder.append(className);
            stringBuilder.append(Constants.PACKAGE_JOINER);
            String methodName = method.getName();
            stringBuilder.append(methodName);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return stringBuilder.toString();
    }
}
